#include "DxEnum.h"
#include <algorithm>
#include <cstring>

cD3D9Enumer g_D3D9Enumer;

cD3D9Enumer* GetEnumer(){
	return &g_D3D9Enumer;
}

HRESULT  cD3D9Enumer::EnumAdapters(LPDIRECT3D9 pD3D9)
{
	m_pD3D9 = pD3D9;
	SetDepthStencilFormatList();
	SetMultiSampleTypeList();
	SetPresentIntervalList();
	int nAdapterCount = m_pD3D9->GetAdapterCount();
	int nAdapterModeCount;

	const D3DFORMAT BackBufferFormatArray[]={
		D3DFMT_A8B8G8R8,
		D3DFMT_X8R8G8B8,
		D3DFMT_X1R5G5B5,
		D3DFMT_R5G6B5,
		D3DFMT_A1R5G5B5,
		D3DFMT_A2R10G10B10
	};
	int nBackBufferFormatArrayCount = sizeof(BackBufferFormatArray)/sizeof(BackBufferFormatArray[0]);

	vector<D3DFORMAT> vecAdapterFormat;
	vecAdapterFormat.clear();

	D3DADAPTER_IDENTIFIER9 eAdapterID9;
	D3DDISPLAYMODE displayMode;
	for(int nAdapter = 0;nAdapter!=nAdapterCount;++nAdapter)
	{
		m_pD3D9->GetAdapterIdentifier(nAdapter,0,&eAdapterID9);
		cD3DAdapterInfo* AdapterInfo = new cD3DAdapterInfo;
		AdapterInfo->AdapterOrdinal = nAdapter;
		AdapterInfo->adapterIdentifier9 = eAdapterID9;

		for(int nFormat = 0; nFormat!=nBackBufferFormatArrayCount;++nFormat)
		{
			nAdapterModeCount = m_pD3D9->GetAdapterModeCount(nAdapter,BackBufferFormatArray[nFormat]);
			for(int nMode = 0;nMode!=nAdapterModeCount;++nMode)
			{
				HRESULT hr = m_pD3D9->EnumAdapterModes(nAdapter,
				BackBufferFormatArray[nFormat],nMode,&displayMode);
				AdapterInfo->vecDisplayMode.push_back(displayMode);
				if(find(vecAdapterFormat.begin(),vecAdapterFormat.end(),displayMode.Format)==vecAdapterFormat.end())
				{
					vecAdapterFormat.push_back(displayMode.Format);
				}
			}
		}
		if(AdapterInfo->vecDisplayMode.size()<=0)
		{
			delete AdapterInfo;
			continue;
		}
		if(FAILED(EnumDevice(AdapterInfo,vecAdapterFormat))){
			delete AdapterInfo;
			continue;
		}
		if(AdapterInfo->vecDeviceInfo.size()>0){
			m_AdapterInfoList.push_back(AdapterInfo);
		}
		else {
			delete AdapterInfo;
			continue;
		}
		MultiByteToWideChar(CP_ACP,0,AdapterInfo->adapterIdentifier9.Description,
			-1,AdapterInfo->szAdapterDescription,100);
	}
	return S_OK;
}

HRESULT cD3D9Enumer::EnumDevice(cD3DAdapterInfo* pAdapterInfo,vector<D3DFORMAT>& vecAdapterFormat)
{
	HRESULT hr;
	D3DDEVTYPE devTypeArray[]=
	{
		D3DDEVTYPE_HAL,
		D3DDEVTYPE_REF,
		D3DDEVTYPE_SW
	};
	UINT nDevTypeCount = sizeof(devTypeArray)/sizeof(devTypeArray[0]);

	for(UINT nDevType = 0;nDevType!=nDevTypeCount;++nDevType)
	{
		cD3DDeviceInfo* pDeviceInfo = new cD3DDeviceInfo;
		pDeviceInfo->AdapterOrdinal = pAdapterInfo->AdapterOrdinal;
		pDeviceInfo->devType = devTypeArray[nDevType];

		if(FAILED(hr = m_pD3D9->GetDeviceCaps(pDeviceInfo->AdapterOrdinal,pDeviceInfo->devType,
										&pDeviceInfo->Caps)))
		{
			delete pDeviceInfo;
			continue;
		}
		if(FAILED(hr = EnumDeviceCombo(pAdapterInfo,pDeviceInfo,vecAdapterFormat))){
			delete pDeviceInfo;
			continue;
		}
		if(pDeviceInfo->vecDeviceSettingsCombo.size()>0)
		{
			pAdapterInfo->vecDeviceInfo.push_back(pDeviceInfo);
		}
		else
		{
			delete pDeviceInfo;
		}
		
		if(pDeviceInfo->Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT){
			pDeviceInfo->vecBehaviorFlags.push_back(D3DCREATE_HARDWARE_VERTEXPROCESSING);
			pDeviceInfo->vecBehaviorFlags.push_back(D3DCREATE_SOFTWARE_VERTEXPROCESSING);
			pDeviceInfo->vecBehaviorFlags.push_back(D3DCREATE_MIXED_VERTEXPROCESSING);
		}
		if(pDeviceInfo->Caps.DevCaps & D3DDEVCAPS_PUREDEVICE){
			pDeviceInfo->vecBehaviorFlags.push_back(D3DCREATE_PUREDEVICE);
		}
			
	}
	return S_OK;
}

HRESULT cD3D9Enumer::EnumDeviceCombo(cD3DAdapterInfo* pAdapterInfo,
						cD3DDeviceInfo* pDeviceInfo,vector<D3DFORMAT>& vecAdapterFormat)
{
	const D3DFORMAT backBufferFormatArray[]=
		{
			D3DFMT_A8R8G8B8,
			D3DFMT_X8R8G8B8,
			D3DFMT_A2R10G10B10,
			D3DFMT_R5G6B5,
			D3DFMT_A1R5G5B5,
			D3DFMT_X1R5G5B5
		};
	const UINT backBufferFormatArrayCount = sizeof(backBufferFormatArray)/sizeof(backBufferFormatArray[0]);

	for(int nFormat = 0;nFormat != vecAdapterFormat.size();++nFormat)
	{
		D3DFORMAT adapterFormat = vecAdapterFormat[nFormat];
		for(UINT iBackBufferFormat = 0;iBackBufferFormat<backBufferFormatArrayCount;++iBackBufferFormat)
		{
			D3DFORMAT backBufferFormat = backBufferFormatArray[iBackBufferFormat];
			for(int nWindowed = 0;nWindowed < 2;++nWindowed)
			{
				if(!nWindowed && pAdapterInfo->vecDisplayMode.size()==0)
					continue;
				if(FAILED(m_pD3D9->CheckDeviceType(pAdapterInfo->AdapterOrdinal,pDeviceInfo->devType,
							adapterFormat,backBufferFormat,nWindowed)))
				{
					continue;
				}
				cD3DDeviceSettingsCombo* pDeviceSettingsCombo = new cD3DDeviceSettingsCombo;

				pDeviceSettingsCombo->AdapterOrdinal = pDeviceInfo->AdapterOrdinal;
				pDeviceSettingsCombo->devType = pDeviceInfo->devType;
				pDeviceSettingsCombo->AdapterFormat = adapterFormat;
				pDeviceSettingsCombo->BackBufferFormat = backBufferFormat;
				pDeviceSettingsCombo->Windowed = (nWindowed!=0);

				BuildDepthStancilFormatList(pDeviceSettingsCombo);
				BuildMultiSampleTypeList(pDeviceSettingsCombo);
				if(pDeviceSettingsCombo->vecMultiSampleType.size()<=0)
				{
					delete pDeviceSettingsCombo;
					continue;
				}
				BuildDSMSConflictsList(pDeviceSettingsCombo);
				BuildPresentIntervalList(pDeviceSettingsCombo,pDeviceInfo);

				pDeviceSettingsCombo->pAdapterInfo = pAdapterInfo;
				pDeviceSettingsCombo->pDeviceInfo = pDeviceInfo;

				pDeviceInfo->vecDeviceSettingsCombo.push_back(pDeviceSettingsCombo);
			}
		}
	}


	return S_OK;
}
void cD3D9Enumer::BuildDepthStancilFormatList(cD3DDeviceSettingsCombo* pDeviceSettingsCombo)
{
	D3DFORMAT depthStencilFmt;
	for(int iDSFormat = 0;iDSFormat < m_DepthStencilFormatList.size();++iDSFormat)
	{
		depthStencilFmt = m_DepthStencilFormatList[iDSFormat];
		if(SUCCEEDED(m_pD3D9->CheckDeviceFormat(pDeviceSettingsCombo->AdapterOrdinal,pDeviceSettingsCombo->devType,pDeviceSettingsCombo->AdapterFormat,
						D3DUSAGE_DEPTHSTENCIL,D3DRTYPE_SURFACE,depthStencilFmt)))
		{
			if(SUCCEEDED(m_pD3D9->CheckDepthStencilMatch(pDeviceSettingsCombo->AdapterOrdinal,pDeviceSettingsCombo->devType,pDeviceSettingsCombo->AdapterFormat,
								pDeviceSettingsCombo->BackBufferFormat,depthStencilFmt)))
			{
				pDeviceSettingsCombo->vecDepthStencilFormat.push_back(depthStencilFmt);
			}
		}
	}

}
void cD3D9Enumer::BuildMultiSampleTypeList(cD3DDeviceSettingsCombo* pDeviceSettingsCombo)
{
	D3DMULTISAMPLE_TYPE msType;
	DWORD msQuality;
	for(int iMSType = 0;iMSType < m_MultiSampleTypeList.size();++iMSType)
	{
		msType = m_MultiSampleTypeList[iMSType];
		if(SUCCEEDED(m_pD3D9->CheckDeviceMultiSampleType(pDeviceSettingsCombo->AdapterOrdinal,pDeviceSettingsCombo->devType,pDeviceSettingsCombo->BackBufferFormat,
						pDeviceSettingsCombo->Windowed,msType,&msQuality)))
		{
			pDeviceSettingsCombo->vecMultiSampleType.push_back(msType);
			if(msQuality)
				pDeviceSettingsCombo->vecMultiSampleQuality.push_back(msQuality);

		}
	}
}
void cD3D9Enumer::BuildPresentIntervalList(cD3DDeviceSettingsCombo* pDeviceSettingsCombo,cD3DDeviceInfo* pDeviceInfo)
{
	UINT PI;
	for(int iPI = 0;iPI < m_PresentIntervalList.size();++iPI)
	{
		PI = m_PresentIntervalList[iPI];
		if(pDeviceSettingsCombo->Windowed)
		{
			if( PI == D3DPRESENT_INTERVAL_TWO ||
				PI == D3DPRESENT_INTERVAL_THREE ||
				PI == D3DPRESENT_INTERVAL_FOUR)
			{
				//These intervals are not supported in windowed mode
				continue;
			}
		}
		// D3DPRESENT_INTERVAL_DEFAULT is zero
		if( PI == D3DPRESENT_INTERVAL_DEFAULT || 
			( pDeviceInfo->Caps.PresentationIntervals & PI ))
		{
			pDeviceSettingsCombo->vecPresentInterval.push_back(PI);
		}
	}
}

void cD3D9Enumer::BuildDSMSConflictsList(cD3DDeviceSettingsCombo* pDeviceSettingsCombo)
{
	cD3DEnumDSMSConflicts DSMSConflict;

	for(int iDS = 0;iDS < pDeviceSettingsCombo->vecDepthStencilFormat.size();++iDS)
	{
		D3DFORMAT dsFmt = pDeviceSettingsCombo->vecDepthStencilFormat[iDS];

		for(int iMS = 0;iMS<pDeviceSettingsCombo->vecMultiSampleType.size();++iMS)
		{
			D3DMULTISAMPLE_TYPE msType = pDeviceSettingsCombo->vecMultiSampleType[iMS];
			if(FAILED(m_pD3D9->CheckDeviceMultiSampleType(pDeviceSettingsCombo->AdapterOrdinal,
					pDeviceSettingsCombo->devType,dsFmt,pDeviceSettingsCombo->Windowed,msType,NULL)))
			{
				DSMSConflict.DSFormat = dsFmt;
				DSMSConflict.MSType = msType;
				pDeviceSettingsCombo->vecDSMSConflictList.push_back(DSMSConflict);
			}
		}
	}
}

void cD3D9Enumer::SetDepthStencilFormatList()
{
	m_DepthStencilFormatList.clear();
	m_DepthStencilFormatList.push_back(D3DFMT_D16);
	m_DepthStencilFormatList.push_back(D3DFMT_D15S1);
	m_DepthStencilFormatList.push_back(D3DFMT_D24X8);
	m_DepthStencilFormatList.push_back(D3DFMT_D24S8);
	m_DepthStencilFormatList.push_back(D3DFMT_D24X4S4);
	m_DepthStencilFormatList.push_back(D3DFMT_D32);
}
void cD3D9Enumer::SetMultiSampleTypeList()
{
	m_MultiSampleTypeList.clear();
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_NONE);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_NONMASKABLE);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_2_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_3_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_4_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_5_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_6_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_7_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_8_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_9_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_10_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_11_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_12_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_13_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_14_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_15_SAMPLES);
	m_MultiSampleTypeList.push_back(D3DMULTISAMPLE_15_SAMPLES);
}
void cD3D9Enumer::SetPresentIntervalList()
{
	m_PresentIntervalList.clear();
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_IMMEDIATE);
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_DEFAULT);
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_ONE);
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_TWO);
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_THREE);
	m_PresentIntervalList.push_back(D3DPRESENT_INTERVAL_FOUR);
}
void cD3D9Enumer::DestroyD3D9Enum()
{
	for(int iIndex = 0; iIndex<m_AdapterInfoList.size();++iIndex)
	{
		if(m_AdapterInfoList[iIndex])
		{
			delete m_AdapterInfoList[iIndex];
			m_AdapterInfoList[iIndex] = NULL;
		}
	}
	m_AdapterInfoList.clear();
}

cD3DAdapterInfo::~cD3DAdapterInfo()
{
	for(int iIndex = 0;iIndex<vecDeviceInfo.size();++iIndex)
	{
		if(vecDeviceInfo[iIndex])
		{
			delete vecDeviceInfo[iIndex];
			vecDeviceInfo[iIndex] = NULL;
		}
	}
	vecDeviceInfo.clear();
}
cD3DDeviceInfo::~cD3DDeviceInfo()
{
	for(int iIndex = 0;iIndex<vecDeviceSettingsCombo.size();++iIndex)
	{
		if(vecDeviceSettingsCombo[iIndex])
		{
			delete vecDeviceSettingsCombo[iIndex];
			vecDeviceSettingsCombo[iIndex] = NULL;
		}
	}
	vecDeviceSettingsCombo.clear();
}
cD3DDeviceSettingsCombo::~cD3DDeviceSettingsCombo()
{
	pAdapterInfo = NULL;
	pDeviceInfo = NULL;
}
